docs(runtime,metadata-protocol): the scope-less booted row is a marketplace / offline import, not a multi-package artifact's module - #16123
Conversation
…tplace / offline import, not a multi-package artifact's module Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…r the next release notes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
📓 Docs Drift CheckThis PR changes 2 package(s): 16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 2 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 163ba578c752310b483f8443ec3bab59dfd96948 && git checkout 163ba578c752310b483f8443ec3bab59dfd96948
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1f2a02ba0d68f2d93c96258c16517dca14f64358 f8e396c99bf4a0bc7de2a2428d27b1c9b8f8130f && git checkout -B drift-repro 1f2a02ba0d68f2d93c96258c16517dca14f64358 && git merge --no-ff f8e396c99bf4a0bc7de2a2428d27b1c9b8f8130f
node scripts/docs-audit/affected-docs.mjs --json 1f2a02ba0d68f2d93c96258c16517dca14f64358
|
…he elevation read moved to Pure line rot from this PR's own comment expansion in packages/runtime/src/domains/packages.ts: the shouldDenyAnonymous read moved from :535 to :543, so row 52's anchor and the read no longer met. Written by `node scripts/check-system-context-census.mjs --fix`; the anchor's number is the only thing that changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
Fixes #14803
Comment and prose only. No predicate, no assertion and no served shape changes; both pin suites the card names stay green as written.
Five platform-side comments attributed the scope-less served row that
isWritablePackageanswersfalsefor to thetype: modulesub-package a multi-package artifact carries. That attribution is false. #14597 corrected it at the fixture (examples/app-multi-package's docblock, README and dogfood harness); this is the platform-side sweep the card fenced out of that PR.The corrected claim, proved here rather than inherited
Measured on
origin/main9b459b791, against the builtdist/of@objectstack/specand@objectstack/objectql, using theapp-multi-packageorders manifest body verbatim (which authors noscopekey):defineStackpath:ArtifactPackageEntrySchema.manifestisManifestSchema(packages/spec/src/stack.zod.ts:233), whosescopeis.default('project')(packages/spec/src/kernel/manifest.zod.ts:319— the card cited:311; the line moved, the default did not). So no package of a compiled artifact is ever scope-less.installPackagestorestoRecordManifest(manifest)(packages/objectql/src/registry.ts:3994,:1250), a key-by-key copy that applies no defaults, so the record has noscopekey at all.A genuinely scope-less row therefore arises only where a manifest reaches the registry without that parse — a marketplace install / offline file import (
manifestService.register(rawBody)toql.registerApp: booted, hence read-only), or a Studio-created base throughPOST /api/v1/packages(body.manifest || bodytoinstallPackage: writable). Every corrected comment now names those two paths.What was kept, because it is true and load-bearing: a scope-less booted package is read-only while a scope-less database base is writable, and only
engine.manifeststells them apart — which is why the server owns the verdict. No paragraph was deleted.Sites, re-located by text on current
origin/mainThe card's line numbers are stale — PR #14767 (merged 2026-09-03) and PR #15592 (merged 2026-09-05) both moved
protocol.ts. Every site below was found by text; the line is the one actually found.packages/runtime/src/domains/packages.tswithWritableVerdictdocblockpackages/runtime/src/domains/packages-writable-verdict.test.tspackages/metadata-protocol/src/protocol.tsgetMetaItemspackages/metadata-protocol/src/get-meta-items-package-writable.test.ts.changeset/packages-read-door-writable-verdict.mdRepresentative before / after, site 4:
Site 6 does not exist, and the deadline it carried has already passed
.changeset/packages-read-door-writable-verdict.mdwas consumed by8a1bad8b8(chore: version packages, 2026-09-04 10:20:24 +0000) and its body is now published release history atpackages/runtime/CHANGELOG.md:1708andpackages/metadata-protocol/CHANGELOG.md:957, under## 17.3.0— a version npm reports asdist-tags.latest. This PR deliberately does not rewrite eitherCHANGELOG.md; the disposition of published release history is filed for a ruling as #16122. What this PR does instead is add a changeset that states the correction, so the retraction reaches the next release notes.Three further sites the card did not enumerate, corrected here
The card asked for the enumeration to be tested. It is short by three, all the same defect class, all in files already claimed by this PR:
packages-writable-verdict.test.ts:120— the Pin 2 test title:pin 2: a booted, SCOPE-LESS module (multi-package artifact sub-package) is writable: false, nowpin 2: a booted, SCOPE-LESS package (marketplace / offline import) is writable: false. No gate or checklist pins that string (grepped whole-tree; the only occurrence was the definition).packages-writable-verdict.test.ts:74-75andget-meta-items-package-writable.test.ts:50— the boot-map comments read "whatObjectQL.registerApprecords for every package of a loaded artifact", sitting directly above a map whose scope-less member cannot have come from one. Both now name the two real boot sources and say which constant is which.No identifier was renamed —
CODE_MODULEand its id stay as they are, because that would be a code change.Clause-② verdict:
no, and here is the measurementBuilt
@objectstack/runtimeand@objectstack/metadata-protocol(with their dependency closures) at this branch's head and grepped every published artifact under each package'sfiles[]:runtime/dist/index.js,index.cjs,index.d.tshandlePackagesRequest= 3 inindex.jsruntime/dist/index.js.map,index.cjs.mapmetadata-protocol/dist/index.js,index.cjs,index.d.tsisWritablePackage= 10 / 5metadata-protocol/dist/index.js.map,index.cjs.mapComments are stripped from the emitted JS and never enter the declaration files; the only published byte movement is inside
sourcesContentof the sourcemaps, which is the source text itself, not a declaration. Reinforcing it for site 1:withWritableVerdictis a non-exported local, andgrep -c withWritableVerdict runtime/dist/index.d.tsis0while that same file carries 419 TSDoc blocks — so TSDoc is retained in general, and this docblock is simply not in the published surface. No exported symbol or signature moves, and no request is newly accepted or rejected.One follow-up commit: a docs anchor this PR's own line growth rotted
Lint & Repo Gateswent red on head33c6aab9aat step Docs anchors resolve to real headings, with two errors that are one problem. ThewithWritableVerdictdocblock above grew by 8 lines, which pushed theec.isSystemelevation read in the same file from:535down to:543— socontent/docs/permissions/system-context.mdx:163(census row 52) anchored a line that is no longer the read (anchor-is-not-a-read-site), and the read had no anchor (site-without-a-row).Reproduced locally at that exact head before touching anything (
pnpm check:system-context-census, exit 1, the same two errors), then repaired with the gate's ownnode scripts/check-system-context-census.mjs --fix, which reported1 anchor(s) rewritten. Its entire output diff is one number in one table cell::543is the real read — confirmed by reading the file at both revisions: onorigin/maintheshouldDenyAnonymous({ userId: ec?.userId, isSystem: ec?.isSystem, method: m })statement is at:535, and on this branch it is at:543. Nothing but the anchor's number changed, andcontent/docs/releases/**was not touched (merge-base diff = six paths, none under it).Verification
Two pin suites, both green as written (no assertion touched):
pnpm --filter @objectstack/runtime --filter @objectstack/metadata-protocol run typecheck— bothDone; runtime's test layer also compiles (check:test-typecheck: OK — 27 file(s) / 191 error(s) / 69 pinned signature(s) held, ratchet unmoved).Gate union re-run after the final commit, at
f8e396c99, each verdict quoted from the gate's own output. Both pin suites were re-run at that same head after the anchor fix and are unchanged (12 passed / 7 passed), so no red was traded for another:check-nul-bytes: OK (scanned 7805 text file(s) ... no raw ASCII control bytes)check-engine-double-contract: OK — 797 pinned, 133 in the DEBT ledger, 3 exemptwhere-matcher conformance holds: 362 matcher(s) discovered, 362 answer the combinator battery correctly or refuse it loudlyOK check:comment-mask-adoption — 14 private comment-stripper(s) ... all 14 recordedcheck-driver-memory-census: OK — every declaration is ledgereddoc authoring guard— three green lines (published skills, spec strings, sibling-package prose ids)OK: 27 package(s) read outside themselves, all declared(check:cross-package-test-inputs)check-dispatcher-error-vocabulary: OK — 65 unregistered code-stamping site(s), all classifiedNo empty-frontmatter changeset introduced by this diffThis diff introduces no major bumpcheck-adr-0087-registration: this PR adds no declared-breaking changesetcheck-system-context-census: OK — 105 elevation read sites in 19 packages across 44 files, all anchored; 140 anchors resolve, 27 declared non-read(plus--self-test: all cases passed)check-doc-anchors: 313 internal #fragment link(s) across 407 source file(s) all resolve to a real heading.mdxedit newly implicates, all green:check:docs-single-h1,check:doc-frontmatter,check:docs-section-name,check:doc-route-spelling --self-test,check:docs-redirects,check:corpus-claim-drift,check:role-word,check:docs-audit-scope,check:docs-transcript-drift,check:merge-driver— 24 gate invocations in total, every exit code captured by redirect-then-readLint was narrowed, and the narrowing is declared.
pnpm exec eslint --no-inline-config --format jsonover the four changed source files: 4 files, 0 errors, 0 warnings. Three pieces of evidence that this is a measurement and not a skip: (i) the population is read from eslint's own configuration, which never enables type-aware linting for any file in this repo — noparserOptions.project, no typed@typescript-eslintrules, stated with its own positive control ineslint.config.mjs:320-335; (ii) the file count is eslint's own--format jsonoutput, not a guess; (iii) with no cross-file type information in play and no lint configuration in the diff (git diff --name-onlyagainst the merge base returns five paths, none of them an eslint or tsconfig file), this change cannot move the verdict on a file it did not touch. The whole-repopnpm lintremains CI's run.Authored by the
domain:engineexecution seat, sessionsession_01ARYe3yQTQCUFm5qPYNgKaJ.Generated by Claude Code